import Device from './Device';
import Event from '../Event';
import Debug from '../tools/Debug';
import Strategies from './Strategies';
import BaseModel from '../BaseModel';
import * as THREE from 'three';

type DeviceOptionWithout_Scene_Id_Group = Omit<Editor.DeviceOption, 'scene' | 'id' | 'group'>;
type SceneOption = Partial<{ scene: import('three').Scene }>;
// 新增设备的参数类型
type AddNewDeviceParams = DeviceOptionWithout_Scene_Id_Group & SceneOption & { id?: number | string };

interface DevcieManagerEvents {
  [DeviceManager.EVENT_DEVICE_MANAGER_UPDATE]: (params: { devices: Array<Editor.Device> }) => void;
}

/**
 * 设备管理器
 */
export default class DeviceManager extends Event<DevcieManagerEvents> {
  static EVENT_DEVICE_MANAGER_UPDATE = 'DEVICE_MANAGER_UPDATE';

  private readonly namespace = 'DeviceManager';

  devicesGroup: THREE.Group = new THREE.Group();

  private _devices: Map<string | number, Device> = new Map();

  devices: Map<string | number, Device>;

  /** 键盘事件的中止控制器 */
  keyboardAbortController: AbortController = null;

  /** 设备管理器配置选项 */
  option: Editor.DeviceManagerOption;

  /** 存储处于编辑态的设备id */
  editingDeviceId: number | string = null;

  /**
   * @param option - 设备管理器配置选项
   */
  constructor(option: Editor.DeviceManagerOption) {
    super();
    this.option = option;
    this._initDevicesProxy();
    this._init(option);
  }

  /** 代理devices */
  private _initDevicesProxy(): void {
    const self = this;
    this.devices = new Proxy(this._devices, {
      get(target, prop, receiver) {
        if (prop === 'set') {
          return function (id: number | string, device: Device) {
            const result = target.set(id, device);
            self._emitEvent(DeviceManager.EVENT_DEVICE_MANAGER_UPDATE, { devices: Array.from(target.values()) });
            return result;
          };
        }
        if (prop === 'delete') {
          return function (id: number | string) {
            const result = target.delete(id);
            self._emitEvent(DeviceManager.EVENT_DEVICE_MANAGER_UPDATE, { devices: Array.from(target.values()) });
            return result;
          };
        }
        const value = Reflect.get(target, prop, receiver);
        if (typeof value === 'function') {
          return value.bind(target);
        }
        return value;
      },
    });
  }

  private _init(option: Editor.DeviceManagerOption): void {
    const { scene, preloadDeviceRawDatas = [], building } = option;
    // 修正group的位置
    building.followBasePoint(this.devicesGroup);
    scene.add(this.devicesGroup);

    preloadDeviceRawDatas.forEach((deviceRawData) => {
      this.createDevice({
        ...deviceRawData,
        /** 预渲染设备显式表明为非新增设备 */
        isNewDevice: false,
      });
    });
  }

  /** 手动触发UPDATE事件 */
  _manualEmitUpadateEvent = () => {
    this._emitEvent(DeviceManager.EVENT_DEVICE_MANAGER_UPDATE, { devices: Array.from(this.devices.values()) });
  };

  followTransfromHandler() {
    const { building } = this.option;
    // 位置跟随矫正
    this.devicesGroup.position.copy(building.model.position);
    // 旋转跟随矫正
    this.devicesGroup.quaternion.copy(building.model.quaternion);
  }

  _followTransfromMouseDownHandler = () => {
    this.devicesGroup.visible = false;
  };
  _followTransfromMouseUpHandler = () => {
    const { building } = this.option;
    // 位置跟随矫正
    this.devicesGroup.position.copy(building.model.position);
    // 旋转跟随矫正
    this.devicesGroup.quaternion.copy(building.model.quaternion);
    this.devicesGroup.visible = true;
  };

  /** 开始deviceGroup跟随变换
   * - 依赖transformControls的监听事件
   */
  enablefollowTransform(): void {
    const { controls } = this.option;
    if (!controls) {
      Debug.error('-enablefollowTransform-未持有controls,请检查配置');
      return;
    }
    const { transformControls } = controls;
    transformControls.addEventListener('mouseDown', this._followTransfromMouseDownHandler);
    transformControls.addEventListener('mouseUp', this._followTransfromMouseUpHandler);
  }

  /** 关闭deviceGroup跟随变换 */
  disablefollowTransform(): void {
    const { controls } = this.option;
    if (!controls) {
      Debug.error('-disablefollowTransform-未持有controls,请检查配置');
      return;
    }
    const { transformControls } = controls;
    transformControls.removeEventListener('mouseDown', this._followTransfromMouseDownHandler);
    transformControls.removeEventListener('mouseUp', this._followTransfromMouseUpHandler);
  }

  /**
   * 创建设备
   * - isNewDevice 标识为是否为新设备，默认为true
   */
  createDevice(deviceOption: AddNewDeviceParams & { isNewDevice?: boolean }): void {
    const { isNewDevice = true, ..._deviceOption } = deviceOption;
    const { scene } = this.option;
    const device = new Device({
      ..._deviceOption,
      group: this.devicesGroup,
      scene,
    });
    device.on(BaseModel.EVENT_BASEMODEL_LOADED, () => {
      this.devices.set(device.id, device);
      isNewDevice && this.openEdit(device, isNewDevice);
    });
  }

  /**
   * 开启设备编辑
   * -  isNewDevice 默认为false，只有createDevice的自动触发时为true
   */
  openEdit(device: Editor.Device, isNewDevice?: boolean): void {
    isNewDevice = isNewDevice || false;
    const { controls } = this.option;
    // 静默关闭一次编辑
    controls.transformControls.enabled && this.closeEdit();
    const enableEditFlow = ({
      device,
      isNewDevice = false,
    }: {
      device: Editor.Device;
      /** 标识是否为新增设备 */
      isNewDevice?: boolean;
    }): void => {
      /** 【设备类型-type】 */
      const type = device.type;
      /** 【贴面类型-sideType】 */
      const sideType = device.sideType;
      this.keyboardAbortController && this.keyboardAbortController.abort();
      this.keyboardAbortController = new AbortController();
      // 应用不同的策略
      const { camera, controls, building } = this.option;
      const { locateStrategies, controlsStrategies, keyboardStrategies } = Strategies;
      const locateStrategy = locateStrategies[type];
      const controlStrategy = controlsStrategies[type];
      const keyboardtrategy = keyboardStrategies[type];
      // 定位
      locateStrategy &&
        locateStrategy({
          sideType, // 贴面类型
          building,
          device,
          camera,
          controls,
          isNewDevice,
        });
      // 控制器
      controlStrategy &&
        controlStrategy({
          controls,
        });
      // 键盘
      keyboardtrategy &&
        keyboardtrategy({
          sideType, // 贴面类型
          controls,
          camera,
          building,
          keyboardAbortController: this.keyboardAbortController,
        });
      // 编辑态的设备id
      this.editingDeviceId = device.id;
    };
    enableEditFlow({ device, isNewDevice });
  }

  /** 关闭设备编辑*/
  closeEdit(): void {
    const { controls, camera } = this.option;
    // 控制器相关
    controls.detachTransfromControls();
    const editDevice = this.devices.get(this.editingDeviceId);
    if (editDevice) {
      Debug.log(this.namespace, 'closeEdit', editDevice.getSaveData());
    }

    // TODO: 这部分要优化 相机回退
    // orbitControls.target = new THREE.Vector3(0, 0, 0);
    // camera.position.set(500 + 200, 1000, 200);

    // 移除键盘事件
    this.keyboardAbortController && this.keyboardAbortController.abort();
    this.keyboardAbortController = null;
  }

  /**
   * 根据ID移除设备
   * @param id - 设备ID
   */
  removeDeviceById(id: number | string): void {
    if (typeof id === 'undefined') return;
    const device = this.devices.get(id);
    if (!device) return;

    // 安全关闭检查
    const { controls } = this.option;
    if (controls.isObjectControlled(device.model)) {
      this.closeEdit();
    }
    // 移除device
    device.baseModelDispose();
    this.devices.delete(id);
  }

  /**
   * 移除所有设备
   */
  removeAllDevices(): void {
    const { controls } = this.option;
    controls.transformControls.enabled && this.closeEdit();
    this.devices.forEach((device, deviceId) => {
      this.removeDeviceById(deviceId);
    });
    this.devices.clear();
  }

  /**
   * 销毁设备管理器
   */
  destroy(): void {
    this.closeEdit();
    this.disablefollowTransform();
    this.removeAllDevices();
    this._removeEvents();
  }
}
